<?php

/**
 * Simple event notification class
 *
 * Example usage:
 * <code>
 * <?php
 * $eventManager = new EventManager();
 * $eventManager->registerEvent('SomeEvent', 'onSomeEvent');
 * $eventManager->addListener('SomeEvent', $someObject);
 * $eventManager->fireEvent('SomeEvent');
 * ?>
 * </code>
 *
 * In your listener class, implement onEvent method:
 * <code>
 * function onSomeEvent($params) {
 *   ... Process event here
 * }
 * </code>
 *
 * Shopalong, the open source shopping system
 * http://shopalong.blogspot.com/
 *
 * @author Stuart Metcalfe <shoopdev@gmail.com>
 * @copyright Copyright &copy; 2006, Shopalong
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */

class EventManager
{
	var $_listeners = array();
	var $_events = array();
	
	/**
	 * Constructor for this class
	 *
	 * @accesss public
	 */
	
	function EventManager()
	{
		// Default event to notify of new events added
		$this->registerEvent('EventRegistered', 'onEventRegistered');
	}
	
	/**
	 * Add an event listener
	 *
	 * @access public
	 * @param string $eventName
	 * @param object $listener
	 */
	
	function addListener($eventName, $listener)
	{
		if (!array_key_exists($eventName, $this->_events))
		{
			// Error: event not registered
			return;
		}
		
		$this->_listeners[$eventName][] = &$listener;
	}
	
	/**
	 * Notify listeners of an event
	 *
	 * @access public
	 * @param string $eventName
	 * @param array $params Associative array of parameters to be passed to the callable method (Default: empty array)
	 */
	
	function fireEvent($eventName, $params=array())
	{
		if (!array_key_exists($eventName, $this->_events))
		{
			// Error: event not registered
			return;
		}
		
		$function = $this->_events[$eventName];
		
		foreach ($this->_listeners[$eventName] as $listener)
		{
			$listener->$function($params);
		}
	}
	
	/**
	 * Register an event with the event manager
	 *
	 * @access public
	 * @param string $eventName Cannot be empty and must contain only
	 * alphanumeric characters
	 * @param string $callableMethodName Must be a valid name for a PHP class
	 * method which listeners must implement including a single $params argument
	 */
	
	function registerEvent($eventName, $callableMethodName)
	{
		if (!ereg('^[a-zA-Z0-9]+$', $eventName))
		{
			// Error: $eventName empty
			return;
		}
		
		if (empty($callableMethodName))
		{
			// Error: $callableMethodName empty
			return;
		}
		
		if (array_key_exists($eventName, $this->_events))
		{
			// Error: event already registered
			return;
		}
		
		$this->_events[$eventName] = $callableMethodName;
		$this->_listeners[$eventName] = array();
		// Notify listeners of a new event registered
		$params = array('eventName'=>$eventName);
		$this->fireEvent('EventRegistered', $params);
	}
	
	/**
	 * Remove an event listener
	 *
	 * @access public
	 * @param object $listener The listener to be removed
	 * @param string $eventName The event to unregister the listener from. 
	 * Default: '' (all events)
	 */
	
	function removeListener($listener, $eventName='')
	{
		if (!empty($eventName) && !array_key_exists($eventName, $this->_events))
		{
			// Error: invalid event specified
			return;
		}
		
		$events = (empty($eventName)) ? $this->_events : array($eventName);
		
		foreach ($events as $event)
		{
			$listenersTmp = array();
			
			foreach ($this->listeners[$event] as $eventListener)
			{
				if (!($eventListener === $listener))
				{
					$listenersTmp[] = $eventListener;
				}
			}
			
			$this->listeners[$event] = $listenersTmp;
		}
	}
	
	/**
	 * Unregisters an event and removes all listeners for that event
	 *
	 * @access public
	 * @param string $eventName
	 */
	
	function unregisterEvent($eventName)
	{
		if (!array_key_exists($eventName, $this->_events))
		{
			// Error: Event not registered
			return;
		}
		
		$eventsTmp = array();
		$listenersTmp = array();
		
		foreach ($this->_events as $event)
		{
			if ($event != $eventName)
			{
				$eventsTmp[$event] = $this->_events[$event];
				$listenersTmp[$event] = $this->_listeners[$event];
			}
		}
		
		$this->_events = $eventsTmp;
		$this->_listeners = $listenersTmp;
	}
}

?>
